package com.dingding.manager.core.aop.log;

import java.lang.reflect.Method;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.dingding.manager.common.beans.SysOptLogRec;
import com.dingding.manager.core.shiro.ShiroKit;
import com.dingding.manager.core.shiro.ShiroUser;
import com.dingding.manager.core.util.IpUtil;
import com.dingding.manager.core.util.ToolUtil;

import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;

/**
 * @ClassName:  LogHandler   
 * @Description:记录系统修改操作日志，存入数据库
 * @author: szc 
 * @date:   2017年1月12日 下午7:50:03
 * @version V2.6.4
 */
@Aspect
@Component
public class LogHandler {

	@Autowired
	private HttpServletRequest request;

	@Pointcut("execution(* com.dingding.manager.*..service.*.*(..))")
	public void logSave() {
	}
	
	/**
	 * @Title: logRecord   
	 * @Description: 使用返回通知记录有改动数据库操作的页面
	 * @author: szc 
	 * @param point 连接点
	 * @param logRecord 标注注解类
	 * @throws Throwable
	 * @date:   2017年1月12日 下午7:51:09   
	 * @since V 2.6.4     
	 */
	@AfterReturning(value = "logSave() && @annotation(logRecord) && args(..)",returning="result")
	public void logRecord(JoinPoint point, LogRecord logRecord,Object result) throws Throwable {
		// 获取连接点的方法签名对象
		MethodSignature methodSignature = (MethodSignature) point.getSignature();

		// 获取拦截方法的所在类
		String className = point.getTarget().getClass().getName();

		// 获取拦截的方法名
		Method method = methodSignature.getMethod();
		String methodName = method.getName();
		
		// 得到方法参数的名称
		String[] paramNames = getFieldsName(this.getClass(), className, methodSignature.getName());
				
		// 获取拦截方法参数
		Object[] params = point.getArgs();
		
		//传入参数拼接值
		String param=null;
		if (ToolUtil.isNotEmpty(params) && ToolUtil.isNotEmpty(paramNames)) {
			// 拼接操作方法传入的参数名称和参数值
			param = joinParams(paramNames, params);
		}

		// 获取客户端ip
		String ip = IpUtil.getIp(request);
		
		//获取操作模块和操作类型
		String moduleName=null;
		String option=null;
		if (ToolUtil.isNotEmpty(logRecord)) {
			moduleName = logRecord.moduleName();
			option = logRecord.option();
		}
		
		// 当前登录用户信息
		ShiroUser user = ShiroKit.getUser();
		if (ToolUtil.isEmpty(user)) {
			return;
		}
		
		//拼接后的操作内容
		String optContent=joinOptContent(param, moduleName, option, user);
		
		// 日志赋值并保存
		SysOptLogRec logInfo = new SysOptLogRec();
		logInfo.setOptClass(className);
		logInfo.setOptMethod(methodName);
		logInfo.setOptModule(moduleName);
		logInfo.setOptType(option);
		logInfo.setParaContent(param);
		logInfo.setOptContent(optContent);
		logInfo.setOptUserId(user.getId());
		logInfo.setOptUserName(user.getLoginName());
		logInfo.setOptUserIp(ip);
		logInfo.setOptTime(new Date());
		//返回通知实在执行成功后才执行，如果执行失败则不会通知，所以此处的操作状态会恒为success
		/*if (ToolUtil.isNotEmpty(result)) {
			logInfo.setOptStatus(result.toString());
		}else{
			logInfo.setOptStatus("success");
		}*/
		logInfo.setOptStatus("success");
		// 保存日志到数据库
		LogFactoryImpl.init().saveLog(logInfo);

	}
	
	/**
	 * @Title: joinOptContent   
	 * @Description: 拼接操作内容
	 * @author: szc 
	 * @param param
	 * @param moduleName 模块名称
	 * @param option  操作类型
	 * @param user	当前登录系统的用户
	 * @date:   2017年1月15日 下午4:11:47   
	 * @since V 2.6.4     
	 * @return: String 拼接后的操作内容
	 */
	private String joinOptContent(String param, String moduleName, String option, ShiroUser user) {
		StringBuilder optContent=new StringBuilder();
		optContent.append(user.loginName);
		optContent.append(" 操作:");
		optContent.append(moduleName);
		optContent.append("->");
		optContent.append(option);
		optContent.append(",操作后的数据为:");
		optContent.append(param);
		return optContent.toString();
	}

	/**
	 * @Title: joinParams
	 * @Description: 拼接参数名称和参数
	 * @author: szc
	 * @param paramNames
	 * @param params
	 * @param param
	 * @date: 2017年1月12日 下午7:32:03
	 * @since V 2.6.4
	 * @return: String 拼接后的完整参数 name:value
	 */
	private String joinParams(String[] paramNames, Object[] params) {
		StringBuffer param = new StringBuffer();
		for (int i = 0; i < params.length; i++) {
			param.append(paramNames[i]);
			param.append(":");
			param.append(params[i].toString());
			// 当i=params.length - 1时， 没有下一个值，末尾不再拼接，
			if (i == params.length - 1) {

			} else {
				param.append(",");
			}
		}
		return param.toString();
	}

	/**
	 * 得到方法参数的名称
	 * @param cls
	 *            当前切面类
	 * @param clazzName
	 *            当前操作方法所在类
	 * @param methodName
	 *            当前操作方法
	 * @return 得到方法参数的名称
	 * @throws NotFoundException
	 * @throws ClassNotFoundException 
	 */
	@SuppressWarnings("rawtypes")
	private static String[] getFieldsName(Class cls, String className, String methodName) throws NotFoundException, ClassNotFoundException {
		Class<?> clazz = Class.forName(className);
		String clazzName = clazz.getName();
		
		ClassPool pool = ClassPool.getDefault();

		ClassClassPath classPath = new ClassClassPath(cls);
		pool.insertClassPath(classPath);

		CtClass cc = pool.get(clazzName);
		CtMethod cm = cc.getDeclaredMethod(methodName);
		MethodInfo methodInfo = cm.getMethodInfo();
		CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
		LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
		if (attr == null) {
			// exception
		}
		String[] paramNames = new String[cm.getParameterTypes().length];
		int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
		for (int i = 0; i < paramNames.length; i++) {
			paramNames[i] = attr.variableName(i + pos); // paramNames即参数名
		}
		return paramNames;
	}

}
